import { _decorator, Component, Node } from 'cc';
import { GameScene } from './GameScene';
const { ccclass, property, disallowMultiple } = _decorator;

export interface SceneMap {
    [key: string]: GameScene;
}

export enum SceneChangeMode {
    HIDE = 0,   // 只把节点隐藏，但是不会销毁场景节点，也不会卸载资源
    DESTROY,    // 销毁场景节点，但是不会卸载资源
    UNLOAD, // 卸载资源，会销毁场景节点
}

export type SceneLoadSuccessCallback = (scene: GameScene) => void;
export type SceneLoadErrorCallback = (err: Error) => void;

@ccclass('SceneManager')
@disallowMultiple
export class SceneManager extends Component {
    // 场景的配置信息，将场景名设置好后一次性初始化到内存中，后续直接使用scene的名字来进行场景的切换
    private _sceneConfig: SceneMap = {};
    // 当前场景栈，在 enter 时入栈，back 时出栈
    private _sceneStack: GameScene[] = [];
    // 场景的回退栈，不销毁场景时，在 back 时入栈，forward 时出栈
    private _sceneBackStack: GameScene[] = [];

    private _inited: boolean = false;
    protected static _instance: SceneManager = null;
    public static instance() {
        return this._instance;
    }

    onLoad() {
        SceneManager._instance = this;
    }

    init(sceneConfig: SceneMap = {}) {
        if (this._inited) {
            return;
        }

        this._inited = true;
        this._sceneConfig = sceneConfig;
    }

    // 当前加载中的所有场景
    scenes() {
        return this._sceneStack;
    }

    currentScene() {
        return this._sceneStack[this._sceneStack.length - 1];
    }

    private formatSceneParam(scene: string | GameScene): GameScene {
        let targetScene: GameScene;
        if (typeof scene === "string") {
            targetScene = this._sceneConfig[scene];
            if (!targetScene) {
                throw new Error("未找到场景：" + scene);
            }
        } else {
            targetScene = this._sceneConfig[scene.name];
            if (!targetScene) {
                targetScene = scene;
                this._sceneConfig[scene.name] = scene;
            }
        }

        return targetScene;
    }

    private onSceneChange(scene: GameScene, mode: SceneChangeMode) {
        if (!scene) {
            return;
        }

        if (mode == SceneChangeMode.HIDE) {
            scene.hide();
        } else if (mode == SceneChangeMode.DESTROY) {
            scene.destroy();
        } else {
            scene.unload();
        }
    }

    /**
     * 进入一个新场景
     * @param scene 场景名称
     * @param onComplete 加载完成回调
     * @param onError 加载失败回调
     * @param destroyScene 切换模式
     **/
    enter(scene: string | GameScene,
        onComplete?: SceneLoadSuccessCallback,
        onError?: SceneLoadErrorCallback,
        mode: SceneChangeMode = SceneChangeMode.HIDE,
    ) {
        let targetScene: GameScene = this.formatSceneParam(scene);
        targetScene.show(() => {
            // 进入新场景，这个场景就是最新的场景，不能再进行前进
            this._sceneBackStack = [];

            this.onSceneChange(this.currentScene(), mode);
            this._sceneStack.push(targetScene);
            onComplete && onComplete(targetScene);
        }, onError);
    }

    /**
     * 替换当前场景，与 enter 相比，他不会清空 _sceneBackStack 栈
     * @param scene 场景
     * @param onComplete 加载完成回调
     * @param onError 加载失败回调
     * @param unloadBundle 是否卸载被替换场景的资源
     */
    replace(scene: string | GameScene,
        onComplete?: SceneLoadSuccessCallback,
        onError?: SceneLoadErrorCallback,
        mode: SceneChangeMode = SceneChangeMode.HIDE,
    ) {
        let targetScene: GameScene = this.formatSceneParam(scene);
        targetScene.load(() => {
            this.onSceneChange(this._sceneStack.pop(), mode);
            this._sceneStack.push(targetScene);
            onComplete && onComplete(targetScene);
        }, onError);
    }

    // 返回上一场景
    back(onComplete?: SceneLoadSuccessCallback,
        onError?: SceneLoadErrorCallback,
        mode: SceneChangeMode = SceneChangeMode.HIDE,
    ) {
        if (this._sceneStack.length <= 1) {
            throw new Error("没有可返回的场景");
        }

        let targetScene = this._sceneStack[this._sceneStack.length - 2];
        targetScene.show(() => {
            let curScene = this._sceneStack.pop();
            this.onSceneChange(curScene, mode);
            this._sceneBackStack.push(curScene);
            onComplete && onComplete(targetScene);
        }, onError);
    }

    // 返回到指定场景
    backTo(sceneName: string,
        onComplete?: SceneLoadSuccessCallback,
        onError?: SceneLoadErrorCallback,
        mode: SceneChangeMode = SceneChangeMode.HIDE,
    ) {
        // 找到目标场景在栈中的位置
        let targetScene: GameScene;
        let sceneIdx: number = -1;
        targetScene = this._sceneStack.find((gameScene: GameScene, index: number) => {
            if (gameScene.name != sceneName) {
                return false;
            }

            sceneIdx = index;
            return true;
        });

        if (sceneIdx == -1) {
            throw new Error(`can't backTo scene: ${sceneName} not instantiate`);
        }

        // 清空目标场景之后的所有场景
        for (let idx = 0; idx < this._sceneStack.length - 1 - sceneIdx; idx++) {
            let scene = this._sceneStack.pop();
            this._sceneBackStack.push(scene);
            this.onSceneChange(scene, mode);
        }

        targetScene.show(() => {
            onComplete && onComplete(targetScene);
        }, onError);
    }

    // 前进
    forward(onComplete?: SceneLoadSuccessCallback,
        onError?: SceneLoadErrorCallback,
        mode: SceneChangeMode = SceneChangeMode.HIDE,
    ) {
        if (this._sceneBackStack.length === 0) {
            throw new Error("没有可前进的场景");
        }

        let targetScene = this._sceneBackStack[this._sceneBackStack.length - 1];
        targetScene.show(() => {
            this.onSceneChange(this.currentScene(), mode);
            this._sceneBackStack.pop();
            this._sceneStack.push(targetScene);
            onComplete && onComplete(targetScene);
        }, onError);
    }

    forwardTo(sceneName: string,
        onComplete?: SceneLoadSuccessCallback,
        onError?: SceneLoadErrorCallback,
        mode: SceneChangeMode = SceneChangeMode.HIDE
    ) {
        // 找到目标场景在栈中的位置
        let targetScene: GameScene;
        let sceneIdx: number = -1;
        targetScene = this._sceneBackStack.find((gameScene: GameScene, index: number) => {
            if (gameScene.name != sceneName) {
                return false;
            }

            sceneIdx = index;
            return true;
        });

        if (sceneIdx == -1) {
            throw new Error(`can't backTo scene: ${sceneName} not instantiate`);
        }

        // 清空目标场景之后的所有场景
        for (let idx = 0; idx < this._sceneBackStack.length - 1 - sceneIdx; idx++) {
            let scene = this._sceneBackStack.pop();
            this._sceneStack.push(scene);
            this.onSceneChange(scene, mode);
        }

        targetScene.show(() => {
            onComplete && onComplete(targetScene);
        }, onError);
    }

    // 清空所有场景，并且重新加载一个场景
    resetTo(scene: string | GameScene,
        onComplete?: SceneLoadSuccessCallback,
        onError?: SceneLoadErrorCallback,
        mode: SceneChangeMode = SceneChangeMode.HIDE
    ) {
        for (let i = 0; i < this._sceneStack.length; i++) {
            this.onSceneChange(this._sceneStack[i], mode);
        }

        let targetScene: GameScene = this.formatSceneParam(scene);
        targetScene.load(() => {
            this._sceneStack.push(targetScene);
            onComplete && onComplete(targetScene);
        }, onError);
    }

    reload(onComplete: SceneLoadSuccessCallback) {
        this.replace(this.currentScene(), onComplete, null, SceneChangeMode.DESTROY);
    }
}
